#include "processorInfo.h"
#include <iostream>
#include <cstring>


void        
ProcessorInfo::setGeneral(UINT8 len, const std::string& socketDesignation, UINT8 cpuType, UINT8 cpuFamily,
                        const std::string& cpuManufacturer, UINT64 cpuID, const std::string& cpuVersion,
                        UINT8 voltage, UINT16 externalClock, UINT16 maxSpeed, UINT16 curSpeed, 
                        UINT8 status, UINT8 cpuUpgrade)
{
    _length = len;
    _socketDesignation = socketDesignation;
    _processorType = cpuType;
    _processorFamily = cpuFamily;
    _processorManufacturer = cpuManufacturer;
    _processorID = cpuID;
    _processorVersion = cpuVersion;
    _voltage = voltage;
    _externalClock = externalClock;
    _maxSpeed = maxSpeed;
    _currentSpeed = curSpeed;
    _status = status;
    _processorUpgrade = cpuUpgrade;
}

void        
ProcessorInfo::set2_1(UINT16 l1, UINT16 l2, UINT16 l3)
{
    _L1CacheHandle = l1;
    _L2CacheHandle = l2;
    _L3CacheHandle = l3;
}
void        
ProcessorInfo::set2_3(const std::string &sn, const std::string &tag, const std::string &pn)
{
    _serialNumber = sn;
    _assetTag = tag;
    _partNumber = pn;
}
void        
ProcessorInfo::set2_5(UINT8 coreCount, UINT8 coreEnable, UINT8 threadCount, UINT16 characteristics)
{
    _coreCount = coreCount;
    _coreEnabled = coreEnable;
    _threadCount = threadCount;
    _processorCharacteristics = characteristics;
}
void        
ProcessorInfo::set3_0(UINT16 coreCount2, UINT16 coreEnabled2, UINT16 threadCount2) 
{
	_coreCount2 = coreCount2;
	_coreEnabled2 = coreEnabled2;
	_threadCount2 = threadCount2;
}

void        
ProcessorInfo::print()
{
    if (_length < 0x1A) 
        return;
    std::cout << "Processor Information:" << std::endl;
    std::cout << "\tSocket Designation: " << _socketDesignation << std::endl;
    std::cout << "\tType: " << getProcessorType() << std::endl;
	std::cout << "\tFamily: " << getProcessorFamily() << std::endl;
	std::cout << "\tManufacturer: " << _processorManufacturer << std::endl;
	printProcessorId();
	std::cout << "\tVersion: " << _processorVersion << std::endl;
	printProcessorVoltage();
	printProcessorFrequency("External Clock", _externalClock);
	printProcessorFrequency("Max Speed", _maxSpeed);
	printProcessorFrequency("Current Speed", _currentSpeed);

	if (_status & (1 << 6))
		printf("\tStatus: Populated, %s\n",
			getProcessorStatus(_status & 0x07));
	else
		printf("\tStatus: Unpopulated\n");
	printf("\tUpgrade: %s\n",getProcessorUpgrade());

	if (_length >= 0x20) {
		printProcessorCache("L1 Cache Handle", _L1CacheHandle, "L1", _tableVersion);
		printProcessorCache("L2 Cache Handle", _L2CacheHandle, "L2", _tableVersion);
		printProcessorCache("L3 Cache Handle", _L3CacheHandle, "L3", _tableVersion);
	}

	if (_length >= 0x23) {
		std::cout << "\tSerial Number: " << _serialNumber << std::endl;
		std::cout << "\tAsset Tag: " << _assetTag << std::endl;
		std::cout << "\tPart Number: " << _partNumber << std::endl;
	}
	if (_length < 0x28)
		return;
	if (_coreCount != 0) {
		printf("\tCore Count: %u\n",
			   _length >= 0x2C && _coreCount == 0xFF ? _coreCount2 : _coreCount);
	}
	if (_coreEnabled != 0)
		printf("\tCore Enabled: %u\n",
			   _length >= 0x2E && _coreEnabled == 0xFF ? _coreEnabled2 : _coreEnabled);
	if (_threadCount != 0)
		printf("\tThread Count: %u\n",
			   _length >= 0x30 && _threadCount == 0xFF ? _threadCount2 : _threadCount);
	if (_length >= 0x32 && _threadEnabled != 0)
		printf("\tThread Enabled: %u\n", _threadEnabled);
	printProcessorCharacteristics();
	if (!_socketType.empty())
		std::cout << "\tSocket Type:" << _socketType << std::endl;
}

void 
ProcessorInfo::printProcessorCharacteristics()
{
	/* 7.5.9 */
	static const char *characteristics[] = {
		"64-bit capable", /* 2 */
		"Multi-Core",
		"Hardware Thread",
		"Execute Protection",
		"Enhanced Virtualization",
		"Power/Performance Control",
		"128-bit Capable",
		"Arm64 SoC ID" /* 9 */
	};

	if ((_processorCharacteristics & 0x00FC) == 0)
		printf("\tCharacteristics: None\n");
	else {
		int i;

		printf("\tCharacteristics:\n");
		for (i = 2; i <= 9; i++)
			if (_processorCharacteristics & (1 << i))
				printf("\t\t%s", characteristics[i - 2]);
		printf("\n");
	}
}

void 
ProcessorInfo::printProcessorCache(const char *attr, UINT16 code, const char *level, UINT16 ver)
{
	if (code == 0xFFFF) {
		if (ver >= 0x0203)
			printf("\t%s: Not Provided\n", attr);
		else
			printf("\t%s: No %s Cache\n", attr, level);
	} else
		printf("\t%s: 0x%04X\n", attr, code);
}

const char *
ProcessorInfo::getProcessorUpgrade()
{
	/* 7.5.5 */
	static const char *upgrade[] = {
		"Other", /* 0x01 */
		"Unknown",
		"Daughter Board",
		"ZIF Socket",
		"Replaceable Piggy Back",
		"None",
		"LIF Socket",
		"Slot 1",
		"Slot 2",
		"370-pin Socket",
		"Slot A",
		"Slot M",
		"Socket 423",
		"Socket A (Socket 462)",
		"Socket 478",
		"Socket 754",
		"Socket 940",
		"Socket 939",
		"Socket mPGA604",
		"Socket LGA771",
		"Socket LGA775",
		"Socket S1",
		"Socket AM2",
		"Socket F (1207)",
		"Socket LGA1366",
		"Socket G34",
		"Socket AM3",
		"Socket C32",
		"Socket LGA1156",
		"Socket LGA1567",
		"Socket PGA988A",
		"Socket BGA1288",
		"Socket rPGA988B",
		"Socket BGA1023",
		"Socket BGA1224",
		"Socket BGA1155",
		"Socket LGA1356",
		"Socket LGA2011",
		"Socket FS1",
		"Socket FS2",
		"Socket FM1",
		"Socket FM2",
		"Socket LGA2011-3",
		"Socket LGA1356-3",
		"Socket LGA1150",
		"Socket BGA1168",
		"Socket BGA1234",
		"Socket BGA1364",
		"Socket AM4",
		"Socket LGA1151",
		"Socket BGA1356",
		"Socket BGA1440",
		"Socket BGA1515",
		"Socket LGA3647-1",
		"Socket SP3",
		"Socket SP3r2",
		"Socket LGA2066",
		"Socket BGA1392",
		"Socket BGA1510",
		"Socket BGA1528",
		"Socket LGA4189",
		"Socket LGA1200",
		"Socket LGA4677",
		"Socket LGA1700",
		"Socket BGA1744",
		"Socket BGA1781",
		"Socket BGA1211",
		"Socket BGA2422",
		"Socket LGA1211",
		"Socket LGA2422",
		"Socket LGA5773",
		"Socket BGA5773",
		"Socket AM5",
		"Socket SP5",
		"Socket SP6",
		"Socket BGA883",
		"Socket BGA1190",
		"Socket BGA4129",
		"Socket LGA4710",
		"Socket LGA7529" /* 0x50 */
	};

	if (_processorUpgrade  >= 0x01 && _processorUpgrade <= 0x50)
		return upgrade[_processorUpgrade - 0x01];
	return OUT_OF_SPEC;
}

/* code is assumed to be a 3-bit value */
const char *
ProcessorInfo::getProcessorStatus(UINT8 code)
{
	static const char *status[] = {
		"Unknown", /* 0x00 */
		"Enabled",
		"Disabled By User",
		"Disabled By BIOS",
		"Idle", /* 0x04 */
		OUT_OF_SPEC,
		OUT_OF_SPEC,
		"Other" /* 0x07 */
	};

	return status[code];
}

void 
ProcessorInfo::printProcessorFrequency(const char *attr, UINT16 code)
{

	if (code) {
		if (attr)
			printf("\t%s: %u MHz\n", attr, code);
		else
			printf("%u MHz\n", code);
	}
	else
	{
		if (attr)
			printf("\t%s: Unknown\n", attr);
		else
			printf("Unknown\n");
	}
}

void 
ProcessorInfo::printProcessorVoltage()
{
	/* 7.5.4 */
	static const char *voltage[] = {
		"5.0 V", /* 0 */
		"3.3 V",
		"2.9 V" /* 2 */
	};
	int i;

	if (_voltage & 0x80)
		printf("\tVoltage: %.1f V", (float)(_voltage & 0x7f) / 10);
	else if ((_voltage & 0x07) == 0x00)
		printf("\tVoltage: Unknown");
	else {
		char voltage_str[18];
		int off = 0;

		for (i = 0; i <= 2; i++)
		{
			if (_voltage & (1 << i)) {
				/* Insert space if not the first value */
				off += sprintf(voltage_str + off,
					       off ? " %s" :"%s",
					       voltage[i]);
			}
		}
		if (off)
			printf("\tVoltage: %s", voltage_str);
	}
	printf("\n");
}

void 
ProcessorInfo::printProcessorId()
{
	/* Intel AP-485 revision 36, table 2-4 */
	static const char *flags[32] = {
		"FPU (Floating-point unit on-chip)", /* 0 */
		"VME (Virtual mode extension)",
		"DE (Debugging extension)",
		"PSE (Page size extension)",
		"TSC (Time stamp counter)",
		"MSR (Model specific registers)",
		"PAE (Physical address extension)",
		"MCE (Machine check exception)",
		"CX8 (CMPXCHG8 instruction supported)",
		"APIC (On-chip APIC hardware supported)",
		NULL, /* 10 */
		"SEP (Fast system call)",
		"MTRR (Memory type range registers)",
		"PGE (Page global enable)",
		"MCA (Machine check architecture)",
		"CMOV (Conditional move instruction supported)",
		"PAT (Page attribute table)",
		"PSE-36 (36-bit page size extension)",
		"PSN (Processor serial number present and enabled)",
		"CLFSH (CLFLUSH instruction supported)",
		NULL, /* 20 */
		"DS (Debug store)",
		"ACPI (ACPI supported)",
		"MMX (MMX technology supported)",
		"FXSR (FXSAVE and FXSTOR instructions supported)",
		"SSE (Streaming SIMD extensions)",
		"SSE2 (Streaming SIMD extensions 2)",
		"SS (Self-snoop)",
		"HTT (Multi-threading)",
		"TM (Thermal monitor supported)",
		NULL, /* 30 */
		"PBE (Pending break enabled)" /* 31 */
	};
	
	const UINT8 *p = (UINT8 *)&_processorID;
	enum cpuidType sig = getCpuidType();
	UINT32 edx;

	/*
	 * This might help learn about new processors supporting the
	 * CPUID instruction or another form of identification.
	 */
	printf("\tID: %02X %02X %02X %02X %02X %02X %02X %02X\n",
			p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);

	printCpuid("\t\tSignature", sig, p);

	if (sig != cpuid_x86_intel && sig != cpuid_x86_amd)
		return;

	edx = DWORD(p + 4);
	if ((edx & 0xBFEFFBFF) == 0)
		printf("\t\tFlags: None");
	else {
		int i;

		printf("\t\tFlags: ");
		for (i = 0; i <= 31; i++)
			if (flags[i] != NULL && edx & (1 << i))
				printf("%s", flags[i]);
	}
	printf("\n");
}

void 
ProcessorInfo::printCpuid(const char *label, enum cpuidType sig, const UINT8 *p)
{
	UINT32 eax, midr, jep106, soc_revision;
	UINT16 dx;

	switch (sig)
	{
		case cpuid_80386:
			dx = WORD(p);
			/*
			 * 80386 have a different signature.
			 */
			printf("%s: Type %u, Family %u, Major Stepping %u, Minor Stepping %u\n",
				   label, dx >> 12, (dx >> 8) & 0xF,
				   (dx >> 4) & 0xF, dx & 0xF);
			return;

		case cpuid_80486:
			dx = WORD(p);
			printf("%s: Type %u, Family %u, Model %u, Stepping %u",
				   label, (dx >> 12) & 0x3, (dx >> 8) & 0xF,
				   (dx >> 4) & 0xF, dx & 0xF);
			return;

		case cpuid_arm_legacy: /* ARM before SOC ID */
			midr = DWORD(p);
			/*
			 * The format of this field was not defined for ARM processors
			 * before version 3.1.0 of the SMBIOS specification, so we
			 * silently skip it if it reads all zeroes.
			 */
			if (midr == 0)
				return;
			printf("%s: Implementor 0x%02x, Variant 0x%x, Architecture %u, Part 0x%03x, Revision %u",
				   label, midr >> 24, (midr >> 20) & 0xF,
				   (midr >> 16) & 0xF, (midr >> 4) & 0xFFF, midr & 0xF);
			return;

		case cpuid_arm_soc_id: /* ARM with SOC ID */
			/*
			 * If Soc ID is supported, the first DWORD is the JEP-106 code;
			 * the second DWORD is the SoC revision value.
			 */
			jep106 = DWORD(p);
			soc_revision = DWORD(p + 4);
			/*
			 * According to SMC Calling Convention (SMCCC) v1.3 specification
			 * (https://developer.arm.com/documentation/den0028/d/), the format
			 * of the values returned by the SMCCC_ARCH_SOC_ID call is as follows:
			 *
			 * JEP-106 code for the SiP (SoC_ID_type == 0)
			 *   Bit[31] must be zero
			 *   Bits[30:24] JEP-106 bank index for the SiP
			 *   Bits[23:16] JEP-106 identification code with parity bit for the SiP
			 *   Bits[15:0] Implementation defined SoC ID
			 *
			 * SoC revision (SoC_ID_type == 1)
			 *   Bit[31] must be zero
			 *   Bits[30:0] SoC revision
			 */
			printf("Signature: JEP-106 Bank 0x%02x Manufacturer 0x%02x, SoC ID 0x%04x, SoC Revision 0x%08x\n",
				(jep106 >> 24) & 0x7F, (jep106 >> 16) & 0x7F, jep106 & 0xFFFF, soc_revision);
			return;

		case cpuid_x86_intel: /* Intel */
			eax = DWORD(p);
			/*
			 * Extra flags are now returned in the ECX register when
			 * one calls the CPUID instruction. Their meaning is
			 * explained in table 3-5, but DMI doesn't support this
			 * yet.
			 */
			printf("%s: Type %u, Family %u, Model %u, Stepping %u\n",
				   label, (eax >> 12) & 0x3,
				   ((eax >> 20) & 0xFF) + ((eax >> 8) & 0x0F),
				   ((eax >> 12) & 0xF0) + ((eax >> 4) & 0x0F),
				   eax & 0xF);
			break;

		case cpuid_x86_amd: /* AMD, publication #25481 revision 2.28 */
			eax = DWORD(p);
			printf("%s: Family %u, Model %u, Stepping %u\n",
				   label, ((eax >> 8) & 0xF) + (((eax >> 8) & 0xF) == 0xF ? (eax >> 20) & 0xFF : 0),
				   ((eax >> 4) & 0xF) | (((eax >> 8) & 0xF) == 0xF ? (eax >> 12) & 0xF0 : 0),
				   eax & 0xF);
			break;

		case cpuid_loongarch: /* LoongArch Reference Manual, volume 1 */
			eax = DWORD(p);
			printf("%s: Processor Identity 0x%08x\n", label, eax);
			break;
		default:
			return;
	}
}

enum cpuidType 
ProcessorInfo::getCpuidType()
{
	const UINT8 *p = (UINT8 *)&_processorID;
	UINT16 type;

	type = (_processorFamily == 0xFE && _length >= 0x2A) ?
		_processorFamily2 : _processorFamily;

	if (type == 0x05) /* 80386 */ {
		return cpuid_80386;
	} else if (type == 0x06) /* 80486 */ {
		UINT16 dx = WORD(p);
		/*
		 * Not all 80486 CPU support the CPUID instruction, we have to find
		 * whether the one we have here does or not. Note that this trick
		 * works only because we know that 80486 must be little-endian.
		 */
		if ((dx & 0x0F00) == 0x0400
		 && ((dx & 0x00F0) == 0x0040 || (dx & 0x00F0) >= 0x0070)
		 && ((dx & 0x000F) >= 0x0003))
			return cpuid_x86_intel;
		else
			return cpuid_80486;
	}
	else if ((type >= 0x100 && type <= 0x102) /* ARM */
	      || (type >= 0x118 && type <= 0x119)) /* ARM */
	{
		/*
		 * The field's format depends on the processor's support of
		 * the SMCCC_ARCH_SOC_ID architectural call. Software can determine
		 * the support for SoC ID by examining the Processor Characteristics field
		 * for "Arm64 SoC ID" bit.
		 */
		if (_length >= 0x28
		 && (_processorCharacteristics & (1 << 9)))
			return cpuid_arm_soc_id;
		else
			return cpuid_arm_legacy;
	}
	else if ((type >= 0x0B && type <= 0x15) /* Intel, Cyrix */
	      || (type >= 0x28 && type <= 0x2F) /* Intel */
	      || (type >= 0xA1 && type <= 0xB3) /* Intel */
	      || type == 0xB5 /* Intel */
	      || (type >= 0xB9 && type <= 0xC7) /* Intel */
	      || (type >= 0xCD && type <= 0xCF) /* Intel */
	      || (type >= 0xD2 && type <= 0xDB) /* VIA, Intel */
	      || (type >= 0xDD && type <= 0xE0)) /* Intel */
		return cpuid_x86_intel;
	else if ((type >= 0x18 && type <= 0x1D) /* AMD */
	      || type == 0x1F /* AMD */
	      || (type >= 0x38 && type <= 0x3F) /* AMD */
	      || (type >= 0x46 && type <= 0x4F) /* AMD */
	      || (type >= 0x66 && type <= 0x6B) /* AMD */
	      || (type >= 0x83 && type <= 0x8F) /* AMD */
	      || (type >= 0xB6 && type <= 0xB7) /* AMD */
	      || (type >= 0xE4 && type <= 0xEF)) /* AMD */
		return cpuid_x86_amd;
	else if ((type >= 0x258 && type <= 0x262) /* Loongarch */
	      || (type >= 0x26C && type <= 0x271)) /* Loongarch */
		return cpuid_loongarch;

	/* neither X86 nor ARM */
	return cpuid_none;
}

const char *
ProcessorInfo::getProcessorFamily()
{
	/* 7.5.2 */
	static struct {
		int value;
		const char *name;
	} family2[] = {
		{ 0x01, "Other" },
		{ 0x02, "Unknown" },
		{ 0x03, "8086" },
		{ 0x04, "80286" },
		{ 0x05, "80386" },
		{ 0x06, "80486" },
		{ 0x07, "8087" },
		{ 0x08, "80287" },
		{ 0x09, "80387" },
		{ 0x0A, "80487" },
		{ 0x0B, "Pentium" },
		{ 0x0C, "Pentium Pro" },
		{ 0x0D, "Pentium II" },
		{ 0x0E, "Pentium MMX" },
		{ 0x0F, "Celeron" },
		{ 0x10, "Pentium II Xeon" },
		{ 0x11, "Pentium III" },
		{ 0x12, "M1" },
		{ 0x13, "M2" },
		{ 0x14, "Celeron M" },
		{ 0x15, "Pentium 4 HT" },
		{ 0x16, "Intel" },

		{ 0x18, "Duron" },
		{ 0x19, "K5" },
		{ 0x1A, "K6" },
		{ 0x1B, "K6-2" },
		{ 0x1C, "K6-3" },
		{ 0x1D, "Athlon" },
		{ 0x1E, "AMD29000" },
		{ 0x1F, "K6-2+" },
		{ 0x20, "Power PC" },
		{ 0x21, "Power PC 601" },
		{ 0x22, "Power PC 603" },
		{ 0x23, "Power PC 603+" },
		{ 0x24, "Power PC 604" },
		{ 0x25, "Power PC 620" },
		{ 0x26, "Power PC x704" },
		{ 0x27, "Power PC 750" },
		{ 0x28, "Core Duo" },
		{ 0x29, "Core Duo Mobile" },
		{ 0x2A, "Core Solo Mobile" },
		{ 0x2B, "Atom" },
		{ 0x2C, "Core M" },
		{ 0x2D, "Core m3" },
		{ 0x2E, "Core m5" },
		{ 0x2F, "Core m7" },
		{ 0x30, "Alpha" },
		{ 0x31, "Alpha 21064" },
		{ 0x32, "Alpha 21066" },
		{ 0x33, "Alpha 21164" },
		{ 0x34, "Alpha 21164PC" },
		{ 0x35, "Alpha 21164a" },
		{ 0x36, "Alpha 21264" },
		{ 0x37, "Alpha 21364" },
		{ 0x38, "Turion II Ultra Dual-Core Mobile M" },
		{ 0x39, "Turion II Dual-Core Mobile M" },
		{ 0x3A, "Athlon II Dual-Core M" },
		{ 0x3B, "Opteron 6100" },
		{ 0x3C, "Opteron 4100" },
		{ 0x3D, "Opteron 6200" },
		{ 0x3E, "Opteron 4200" },
		{ 0x3F, "FX" },
		{ 0x40, "MIPS" },
		{ 0x41, "MIPS R4000" },
		{ 0x42, "MIPS R4200" },
		{ 0x43, "MIPS R4400" },
		{ 0x44, "MIPS R4600" },
		{ 0x45, "MIPS R10000" },
		{ 0x46, "C-Series" },
		{ 0x47, "E-Series" },
		{ 0x48, "A-Series" },
		{ 0x49, "G-Series" },
		{ 0x4A, "Z-Series" },
		{ 0x4B, "R-Series" },
		{ 0x4C, "Opteron 4300" },
		{ 0x4D, "Opteron 6300" },
		{ 0x4E, "Opteron 3300" },
		{ 0x4F, "FirePro" },
		{ 0x50, "SPARC" },
		{ 0x51, "SuperSPARC" },
		{ 0x52, "MicroSPARC II" },
		{ 0x53, "MicroSPARC IIep" },
		{ 0x54, "UltraSPARC" },
		{ 0x55, "UltraSPARC II" },
		{ 0x56, "UltraSPARC IIi" },
		{ 0x57, "UltraSPARC III" },
		{ 0x58, "UltraSPARC IIIi" },

		{ 0x60, "68040" },
		{ 0x61, "68xxx" },
		{ 0x62, "68000" },
		{ 0x63, "68010" },
		{ 0x64, "68020" },
		{ 0x65, "68030" },
		{ 0x66, "Athlon X4" },
		{ 0x67, "Opteron X1000" },
		{ 0x68, "Opteron X2000" },
		{ 0x69, "Opteron A-Series" },
		{ 0x6A, "Opteron X3000" },
		{ 0x6B, "Zen" },

		{ 0x70, "Hobbit" },

		{ 0x78, "Crusoe TM5000" },
		{ 0x79, "Crusoe TM3000" },
		{ 0x7A, "Efficeon TM8000" },

		{ 0x80, "Weitek" },

		{ 0x82, "Itanium" },
		{ 0x83, "Athlon 64" },
		{ 0x84, "Opteron" },
		{ 0x85, "Sempron" },
		{ 0x86, "Turion 64" },
		{ 0x87, "Dual-Core Opteron" },
		{ 0x88, "Athlon 64 X2" },
		{ 0x89, "Turion 64 X2" },
		{ 0x8A, "Quad-Core Opteron" },
		{ 0x8B, "Third-Generation Opteron" },
		{ 0x8C, "Phenom FX" },
		{ 0x8D, "Phenom X4" },
		{ 0x8E, "Phenom X2" },
		{ 0x8F, "Athlon X2" },
		{ 0x90, "PA-RISC" },
		{ 0x91, "PA-RISC 8500" },
		{ 0x92, "PA-RISC 8000" },
		{ 0x93, "PA-RISC 7300LC" },
		{ 0x94, "PA-RISC 7200" },
		{ 0x95, "PA-RISC 7100LC" },
		{ 0x96, "PA-RISC 7100" },

		{ 0xA0, "V30" },
		{ 0xA1, "Quad-Core Xeon 3200" },
		{ 0xA2, "Dual-Core Xeon 3000" },
		{ 0xA3, "Quad-Core Xeon 5300" },
		{ 0xA4, "Dual-Core Xeon 5100" },
		{ 0xA5, "Dual-Core Xeon 5000" },
		{ 0xA6, "Dual-Core Xeon LV" },
		{ 0xA7, "Dual-Core Xeon ULV" },
		{ 0xA8, "Dual-Core Xeon 7100" },
		{ 0xA9, "Quad-Core Xeon 5400" },
		{ 0xAA, "Quad-Core Xeon" },
		{ 0xAB, "Dual-Core Xeon 5200" },
		{ 0xAC, "Dual-Core Xeon 7200" },
		{ 0xAD, "Quad-Core Xeon 7300" },
		{ 0xAE, "Quad-Core Xeon 7400" },
		{ 0xAF, "Multi-Core Xeon 7400" },
		{ 0xB0, "Pentium III Xeon" },
		{ 0xB1, "Pentium III Speedstep" },
		{ 0xB2, "Pentium 4" },
		{ 0xB3, "Xeon" },
		{ 0xB4, "AS400" },
		{ 0xB5, "Xeon MP" },
		{ 0xB6, "Athlon XP" },
		{ 0xB7, "Athlon MP" },
		{ 0xB8, "Itanium 2" },
		{ 0xB9, "Pentium M" },
		{ 0xBA, "Celeron D" },
		{ 0xBB, "Pentium D" },
		{ 0xBC, "Pentium EE" },
		{ 0xBD, "Core Solo" },
		/* 0xBE handled as a special case */
		{ 0xBF, "Core 2 Duo" },
		{ 0xC0, "Core 2 Solo" },
		{ 0xC1, "Core 2 Extreme" },
		{ 0xC2, "Core 2 Quad" },
		{ 0xC3, "Core 2 Extreme Mobile" },
		{ 0xC4, "Core 2 Duo Mobile" },
		{ 0xC5, "Core 2 Solo Mobile" },
		{ 0xC6, "Core i7" },
		{ 0xC7, "Dual-Core Celeron" },
		{ 0xC8, "IBM390" },
		{ 0xC9, "G4" },
		{ 0xCA, "G5" },
		{ 0xCB, "ESA/390 G6" },
		{ 0xCC, "z/Architecture" },
		{ 0xCD, "Core i5" },
		{ 0xCE, "Core i3" },
		{ 0xCF, "Core i9" },

		{ 0xD2, "C7-M" },
		{ 0xD3, "C7-D" },
		{ 0xD4, "C7" },
		{ 0xD5, "Eden" },
		{ 0xD6, "Multi-Core Xeon" },
		{ 0xD7, "Dual-Core Xeon 3xxx" },
		{ 0xD8, "Quad-Core Xeon 3xxx" },
		{ 0xD9, "Nano" },
		{ 0xDA, "Dual-Core Xeon 5xxx" },
		{ 0xDB, "Quad-Core Xeon 5xxx" },

		{ 0xDD, "Dual-Core Xeon 7xxx" },
		{ 0xDE, "Quad-Core Xeon 7xxx" },
		{ 0xDF, "Multi-Core Xeon 7xxx" },
		{ 0xE0, "Multi-Core Xeon 3400" },

		{ 0xE4, "Opteron 3000" },
		{ 0xE5, "Sempron II" },
		{ 0xE6, "Embedded Opteron Quad-Core" },
		{ 0xE7, "Phenom Triple-Core" },
		{ 0xE8, "Turion Ultra Dual-Core Mobile" },
		{ 0xE9, "Turion Dual-Core Mobile" },
		{ 0xEA, "Athlon Dual-Core" },
		{ 0xEB, "Sempron SI" },
		{ 0xEC, "Phenom II" },
		{ 0xED, "Athlon II" },
		{ 0xEE, "Six-Core Opteron" },
		{ 0xEF, "Sempron M" },

		{ 0xFA, "i860" },
		{ 0xFB, "i960" },

		{ 0x100, "ARMv7" },
		{ 0x101, "ARMv8" },
		{ 0x102, "ARMv9" },
		{ 0x103, "ARM" },
		{ 0x104, "SH-3" },
		{ 0x105, "SH-4" },
		{ 0x118, "ARM" },
		{ 0x119, "StrongARM" },
		{ 0x12C, "6x86" },
		{ 0x12D, "MediaGX" },
		{ 0x12E, "MII" },
		{ 0x140, "WinChip" },
		{ 0x15E, "DSP" },
		{ 0x1F4, "Video Processor" },

		{ 0x200, "RV32" },
		{ 0x201, "RV64" },
		{ 0x202, "RV128" },

		{ 0x258, "LoongArch" },
		{ 0x259, "Loongson 1" },
		{ 0x25A, "Loongson 2" },
		{ 0x25B, "Loongson 3" },
		{ 0x25C, "Loongson 2K" },
		{ 0x25D, "Loongson 3A" },
		{ 0x25E, "Loongson 3B" },
		{ 0x25F, "Loongson 3C" },
		{ 0x260, "Loongson 3D" },
		{ 0x261, "Loongson 3E" },
		{ 0x262, "Dual-Core Loongson 2K 2xxx" },
		{ 0x26C, "Quad-Core Loongson 3A 5xxx" },
		{ 0x26D, "Multi-Core Loongson 3A 5xxx" },
		{ 0x26E, "Quad-Core Loongson 3B 5xxx" },
		{ 0x26F, "Multi-Core Loongson 3B 5xxx" },
		{ 0x270, "Multi-Core Loongson 3C 5xxx" },
		{ 0x271, "Multi-Core Loongson 3D 5xxx" },
	};
	/*
	 * Note to developers: when adding entries to this list, check if
	 * function dmi_processor_id below needs updating too.
	 */

    unsigned int i, low, high;
	UINT16 code;

	/* Special case for ambiguous value 0x30 (SMBIOS 2.0 only) */
	if (_tableVersion == 0x0200 && _processorFamily == 0x30 && _length >= 0x08)
	{
		std::string manufacturer = _processorManufacturer;

		if (strstr(manufacturer.c_str(), "Intel") != NULL
		 || strncasecmp(manufacturer.c_str(), "Intel", 5) == 0)
			return "Pentium Pro";
	}

	code = (_processorFamily == 0xFE &&_length >= 0x2A) ?
		_processorFamily2 : _processorFamily;

	/* Special case for ambiguous value 0xBE */
	if (code == 0xBE) {
		if (_length >= 0x08) {
			std::string manufacturer = _processorManufacturer;

			/* Best bet based on manufacturer string */
			if (strstr(manufacturer.c_str(), "Intel") != NULL
			 || strncasecmp(manufacturer.c_str(), "Intel", 5) == 0)
				return "Core 2";
			if (strstr(manufacturer.c_str(), "AMD") != NULL
			 || strncasecmp(manufacturer.c_str(), "AMD", 3) == 0)
				return "K7";
		}

		return "Core 2 or K7";
	}

	/* Perform a binary search */
	low = 0;
	high = ARRAY_SIZE(family2) - 1;

	while (1)
	{
		i = (low + high) / 2;
		if (family2[i].value == code)
			return family2[i].name;
		if (low == high) /* Not found */
			return OUT_OF_SPEC;

		if (code < family2[i].value)
			high = i;
		else
			low = i + 1;
	}
}

const char* 
ProcessorInfo::getProcessorType()
{
	/* 7.5.1 */
	static const char *type[] = {
		"Other", /* 0x01 */
		"Unknown",
		"Central Processor",
		"Math Processor",
		"DSP Processor",
		"Video Processor" /* 0x06 */
	};

	if (_processorType >= 0x01 && _processorType <= 0x06)
		return type[_processorType - 0x01];
	return OUT_OF_SPEC;
}
